package com.morningstar.qs.api.basic.common.config;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.xml.sax.SAXException;

import com.mstar.qs.common.Entry;
import com.mstar.qs.common.EntryHelper;
import com.mstar.qs.common.io.XmlUtil;

/**
 * indicate the global config file. the root element is env element. so xpath
 * starts with /env
 * 
 * @author dchen2
 * 
 */
public class GlobalConfig {

	private static Entry root = null;
	private static GlobalConfig instance = new GlobalConfig();

	private GlobalConfig() {
	}

	public static GlobalConfig getInstance() {
		return instance;
	}

	private final static Logger log = Logger.getLogger(GlobalConfig.class
			.getPackage().getName());

	/**
	 * traverse element with sub element name "item". If Entry firstly meet the
	 * argument, it return at once.
	 * 
	 * @param attribute
	 *            with in module "item"
	 * @return
	 */
	@Deprecated
	public String getProperty(String key) {
		if (key == null)
			throw new IllegalArgumentException();
		if (root == null || root.empty()) {
			throw new NullPointerException("Not Init global config");
		}
		List<Entry> es = root.retrieveSubEntries("item");
		return retrieveItems(es, key);
	}

	@Deprecated
	private String retrieveItems(List<Entry> es, String key) {
		for (Entry e : es) {
			String k = e.attribute("key");
			if (key.equals(k))
				return e.attribute("value");
		}
		return null;
	}

	/*
	 * public static List<ConfigEntries> getEntries(String elName) { if (elName
	 * == null) throw new IllegalArgumentException(); if (config == null ||
	 * config.empty()) {
	 * System.err.println("      init global config from method---");
	 * configure(); } return config.getEntries(elName); }
	 */
	/**
	 * traverse element with sub element name moduleName. If Entry firstly meet
	 * the argument, it return at once.
	 * 
	 * @param moduleName
	 * @param attribute
	 * @return
	 */

	public static String getProperty(String moduleName, String attribute) {
		if (attribute == null)
			throw new IllegalArgumentException();
		if (root == null || root.empty()) {
			throw new IllegalArgumentException(
					"Global config des not been initialized.");
		}
		return EntryHelper.traverseAttribute(moduleName, root, attribute);
	}

	public static String getSecondaryProperty(String firstModuleName,
			String secondModuleName, String attribute) {
		if (attribute == null || firstModuleName == null
				|| secondModuleName == null)
			throw new NullPointerException("[firstModuleName: "
					+ firstModuleName + "][secondModuleName:"
					+ secondModuleName + "][attribute:" + attribute + "]");
		if (root == null || root.empty())
			throw new IllegalArgumentException(
					"Global config des not been initialized.");
		Entry en = entry(firstModuleName);
		if (en == null) {
			log.info("not found out the firstModulename: " + firstModuleName);
			return null;
		}
		Entry sen = en.secondEntrie(secondModuleName);
		if (sen == null) {
			log.info("not found out the secondModuleName: " + secondModuleName);
			return null;
		} else
			return sen.attribute(attribute);

	}

	public static void addAttribute(String attribute, String value) {
		if (attribute == null)
			throw new IllegalArgumentException();
		if (root == null || root.empty()) {
			throw new NullPointerException("Not initialize global config file.");
		}
		root.addAttribete(attribute, value);
	}

	public static String attribute(String attribute) {

		if (attribute == null)
			throw new NullPointerException("attribute:" + attribute);
		if (root == null || root.empty()) {
			System.err.println("      init global config from method---");
			configure();
		}
		return root.attribute(attribute);
	}

	public static String getText() {

		if (root == null || root.empty()) {
			System.err.println("      init global config from method---");
			configure();
		}
		return root.getValue();
	}

	public static String attributeOfModule(String moduleName, String attribute) {
		Entry e = entry(moduleName);
		if (e != null)
			return e.attribute(attribute);
		else
			return null;
	}

	public static String attributeOfSubModule(String moduleName,
			String subModuleName, String attribute) {
		return attributeCross(root.getElementName() + "/" + moduleName + "/"
				+ subModuleName, attribute);
	}

	public static String attributeCross(String xpath, String attribute) {

		if (xpath == null || attribute == null)
			throw new NullPointerException("attribute:" + attribute);
		if (root == null || root.empty()) {
			System.err.println("      init global config from method---");
			configure();
		}
		if (xpath.indexOf("/") == 0)
			xpath = xpath.substring(1);

		String[] xp = xpath.split(EntryHelper.pe);
		if (xp.length < 2)
			throw new IllegalArgumentException("xpath:" + xpath);

		return EntryHelper.attributeCross(root, xpath, attribute);
	}

	public static String textOfSubModule(String moduleName, String subModuleName) {
		return textCross(root.getElementName() + "/" + moduleName + "/"
				+ subModuleName);
	}

	public static String textOfModule(String moduleName) {
		Entry e = entry(moduleName);
		if (e != null)
			return e.getValue();
		else
			return null;
	}

	public static String textCross(String xpath) {
		if (xpath == null)
			throw new IllegalArgumentException();
		if (root == null || root.empty()) 
			throw new NullPointerException("Global does not initialize.");

		if (xpath.indexOf("/") == 0)
			xpath = xpath.substring(1);

		String[] xp = xpath.split(EntryHelper.pe);
		if (xp.length < 2)
			throw new IllegalArgumentException("xpath:" + xpath);
		return EntryHelper.textCross(root, xpath);
	}

	public static List<Entry> entriesCross(String xpath) {
		if (xpath == null)
			throw new IllegalArgumentException();
		if (root == null || root.empty()) {
			System.err.println("      init global config from method---");
			throw new NullPointerException("Init file is not initiated,");
		}

		if (xpath.indexOf("/") == 0)
			xpath = xpath.substring(1);

		String[] xp = xpath.split(EntryHelper.pe);
		if (xp.length < 2)
			throw new IllegalArgumentException("xpath:" + xpath);
		if (xp[0].equals(root.getRootName())) {
			xpath = xpath.substring(xpath.indexOf("/") + 1);
		}
		return EntryHelper.entriesCross(root, xpath);
	}

	public static List<Entry> entries(String moduleName) {
		if (root == null)
			return null;

		List<Entry> le = root.retrieveSubEntries(moduleName);
		return le;
	}

	public static void combine(String moduleName) {
		root.combineSubEntries(moduleName);
	}

	public static Entry entry(String moduleName) {
		if (root == null || root.empty()) {
			System.err.println("      init global config from method---");
			return null;
		}
		List<Entry> le = root.retrieveSubEntries(moduleName);
		if (le != null && le.size() > 0)
			return le.get(0);
		return null;
	}

	private static void configure() {
		String file = null;
		if (System.getProperty("os.name").toString().indexOf("Windows") != -1) {
			file = GLOBAL_CONFIG_FILE_PATH_WINDOW;
		} else {
			throw new RuntimeException(
					"In order to avoid configuration files confusing, please explicitly invoke configure(String abstractConfigurationFile) to init global configuration.");
		}
		String configFile;
		try {
			configFile = obtainConfigFile(file + "/" + GLOBAL_CONFIG_ENV);
			configure(configFile);
		} catch (Exception e) {
			e.printStackTrace();
			log.fatal("!!!!!!!!!!!!   Failed to get config file...");
		}

	}

	private static String obtainConfigFile(String filepath)
			throws ParserConfigurationException, SAXException, IOException {
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		DocumentBuilder builder = factory.newDocumentBuilder();

		Document doc = builder.parse(new File(filepath));

		Element root = doc.getDocumentElement();
		NamedNodeMap nnm = root.getAttributes();
		String configfile = nnm.getNamedItem("global-config-file")
				.getNodeValue();
		return configfile;
	}

	public static void configure(String globalConfFile) throws Exception {
		java.util.logging.Logger l = java.util.logging.Logger
				.getLogger(GlobalConfig.class.getName());
		l.warning("\t\t -------- Start to init global configuration ... "
				+ globalConfFile);
		Entry en;
		try {
			en = XmlUtil.parseWithAbstractFile(globalConfFile);
			Map<String,String> rootAtt=en.getAttributes();
			String rootName = en.getElementName();
			if (en != null) {
				List<Entry> le = en.retrieveSubEntries("env");
				if (le != null)
					en = le.get(0);
			}
			if (root != null)
				root.corssApplication(en);
			else {

				root = en;
				root.setRootName(rootName);
				root.setElementName(rootName);
				if(rootAtt.size()>0) root.getAttributes().putAll(rootAtt);

			}
			if (root == null) {
				l.warning("\t\t -------- Failed to init global config with "
						+ globalConfFile);
				log.fatal("\t\t -------- Failed to init global config with "
						+ globalConfFile);
			}
			// System.out.println("--------- " + GlobalConfig.getEnv());
			l.warning("\t\t--------- Successfully initialize global configuration file - ");
			log.info("\t\t--------- Successfully initialize global configuration file - "
					+ globalConfFile);
			// System.out.println(root.toString());
		} catch (Exception e) {
			e.printStackTrace();
			throw e;

		}

	}

	@Deprecated
	public static String getEnv() {
		return attribute("type");
	}
	
	public static final String LINE_SEPARATOR = System.getProperty("line.separator");
	public static final String FILE_PATH_SEPARATOR = System.getProperty("file.separator");

	public static final String HOST = "host";
	public static final String PORT = "port";
	public static final String SCHEMA = "schema";
	public static final String USER = "user";
	public static final String PWD = "pwd";
	public static final String URL = "url";
	public static final String PATH = "path";
	public static final String PARAM = "param";
	public static final String QS_DATABASE = "qs-database";
	public static final String XM_SERVICE = "xml-service";
	public static final String SESSION_SERVER = "session-server";
	public static final String BACK_DOOR = "back-door";
	public static final String HOLIDAY = "holiday";
	public static final String GLOBAL_LOGIN_WEB_SERVICE = "global-login-web-service";
	public static final String GLOBE_DATABASE = "globe-database";
	public static final String HIBERNATE = "hibernate";
	public static final String CONF_FILE = "conf-file";
	public static final String BATCH_NET_DATABASE = "batch-net-database";
	public static final String DOTCOM_MARKET_FAIR_VALUE_DATABASE = "dotcom-market-fair-value-database";
	public static final String CALENDAR_SERVICE = "calendar-service";
	public static final String NEWS_SERVICE = "news-service";
	public static final String COMMODITY_SERVICE = "commodity-service";
	public static final String CAPTURE_IMAGE = "capture-image";
	public static final String STORAGE_SERVICE = "storage-service";
	public static final String PORTFOLIO_SERVICE = "PORTFOLIO-service";
	public static final String ID_SERVICE = "ID-service";
	public static final String AUTO_COMPLETE = "Auto-complete";
	public static final String STATIC_DATA_SERVICE = "static-data-service";
	public static final String TIME_SERIES_DATA_SERVICE = "time-series-data-service";
	public static final String AUTHENTICATION = "authentication";
	public static final String WEB_SESSION_CLIENT = "web-session-client";
	public static final String USAGE_TRACKING = "usage-tracking";
	public static final String TERMINAL_BOND_MARKET_WIDGET_DATA_SOURCE_URL = "Terminal-Bond-Market-widget-data-source-url";
	public static final String QSDATA_WEBSERVIE = "QSData-webservie";
	public static final String FRONT_END_CONFIGURATION = "front-end-configuration";
	public static final String PUSHDOMAIN = "pushDomain";
	public static final String SERVERDOMAIN = "serverDomain";
	public static final String PULLSEPARATOR = "pullSeparator";
	public static final String PUSHSEPARATOR = "pushSeparator";
	public static final String ISMAINTAINING = "IsMaintaining";
	public static final String VERSION = "Version";
	public static final String SERVERID = "ServerID";
	public static final String SERVERURL = "ServerURL";
	public static final String WSIMAGESERVERFOLDER = "WsImageServerFolder";
	public static final String WSIMAGEURL = "WsImageURL";
	public static final String CSSREFERENCE = "CSSreference";
	public static final String JSREFERENCE = "JSreference";
	public static final String CSSREFERENCE2 = "CSSreference2";
	public static final String JSREFERENCE2 = "JSreference2";

	public static final String ENV_DEV = "dev";
	public static final String ENV_DEBUG = "debug";
	public static final String ENV_STAGE = "stage";
	public static final String ENV_PRE_STAGE = "prestage";
	public static final String ENV_PRE_LIVE = "pre";
	public static final String ENV_LIVE = "live";

	public static final String GLOBAL_CONFIG_FILE_PATH_WINDOW = "D:/data/conf";
	public static final String GLOBAL_CONFIG_FILE_PATH_LINUX = "/data/conf";
	private static final String GLOBAL_CONFIG_ENV = "global-env.xml";
	public static final String IDENTIFIER_OF_GLOBAL_CONFIG = "global-config";
	public static final String IDENTIFIER_OF_LOG4J = "log4j";

}
